package cn.tworice.log.aspect;

import cn.tworice.log.dao.po.OperationLogDO;
import cn.tworice.common.util.StringUtils;
import cn.tworice.ip.util.IpAddrUtils;
import cn.tworice.log.annotation.Log;
import cn.tworice.log.service.LogService;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.UUID;

/**
 * 日志记录器
 * @author 二饭 [2023/1/29]
 **/
@Component
@Aspect
@Slf4j
public class LogHandleAspect {

    @Resource
    private LogService logService;

    @Pointcut("@annotation(io.swagger.annotations.ApiOperation)")
    public void pointCut() {}

    @Around("pointCut()")
    public Object handle(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        return proceedingJoinPoint.proceed();
    }

    /**
     *  前置通知
     *  没有返回值
     */
    @Before(value = "pointCut()")
    public void before(JoinPoint joinPoint){
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert requestAttributes != null;

        String adminID  = requestAttributes.getRequest().getHeader("adminID");
        if (adminID == null) {
            return;
        }


        try {
            Class<?> clazz = joinPoint.getTarget().getClass();
            if(clazz.getAnnotation(Log.class)!=null && clazz.getAnnotation(Log.class).ignore()){
                // 如果日志排除则不进行记录
                return;
            }
            if (clazz.getAnnotation(Api.class) != null) {
                Api api = (Api) clazz.getAnnotation(Api.class);
                if (api.tags() == null || api.tags().length == 0) {
                    return;
                }
                String apiValue = api.tags()[0];
                String methodName = joinPoint.getSignature().getName();
                Class[] parameterTypes = ((MethodSignature)joinPoint.getSignature()).getMethod().getParameterTypes();
                Method method = clazz.getMethod(methodName,parameterTypes);
                // Get请求不记录操作日志
                if (method.getAnnotation(GetMapping.class) == null) {
                    ApiOperation operation = method.getAnnotation(ApiOperation.class);
                    String operationValue = operation.value();
                    String log = apiValue + "-" + operationValue ;
                    LogHandleAspect.log.info("操作日志："+log);

                    OperationLogDO adminLog = new OperationLogDO();
                    adminLog.setId(UUID.randomUUID().toString().replaceAll("-",""));
                    adminLog.setActive(log);
                    adminLog.setUserId(adminID);
                    adminLog.setCreateTime(System.currentTimeMillis());
                    adminLog.setIpAddr(IpAddrUtils.getIpAddr(requestAttributes.getRequest()));
                    // 用于数据恢复
                    if (method.getAnnotation(DeleteMapping.class) != null && clazz.getAnnotation(Log.class) != null) {
                        Log logAnnotation =(Log) clazz.getAnnotation(Log.class);
                        if(!logAnnotation.ignore() && !StringUtils.isEmpty(logAnnotation.table())){
                            adminLog.setTableName(logAnnotation.table());
                            // TODO 默认参数只有一个 或 第一个参数为要删除的ID数组
                            adminLog.setTargetId(JSON.toJSONString(joinPoint.getArgs()[0]));
                        }
                    }
                    logService.addAdminLog(adminLog);
                }
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    private String filter(String target){
        return target.replace("#", "%23")
                .replace("$","%24")
                .replace("--", "%2D%2D");
    }
}
